package com.lspshoping.util;


import com.lspshoping.annotations.Select;
import com.lspshoping.io.Resources;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

public class XMLConfigBuilder {

    protected static Configuration configuration = new Configuration();

    public static Configuration loadConfiguration(InputStream inputStream){
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(inputStream);
            Element rootElement = document.getRootElement();

            List<Element> propertiesElements = rootElement.selectNodes("//properties");
            if(propertiesElements.size()>0){
                for (Element propertiesElement : propertiesElements) {
                    Attribute resource = propertiesElement.attribute(0);
                    if(resource == null){
                        continue;
                    }
                    String value = resource.getValue();
                    String temp = "/" + value;
                    InputStream in = XMLConfigBuilder.class.getResourceAsStream(temp);
                    Properties properties = new Properties();
                    properties.load(in);
                    configuration.setDriver( properties.getProperty("jdbc.driverClass"));
                    configuration.setUrl( properties.getProperty("jdbc.url"));
                    configuration.setUsername( properties.getProperty("jdbc.username"));
                    configuration.setPassword( properties.getProperty("jdbc.password"));
                }
            }else{
                List<Element> propertyElements = rootElement.selectNodes("//property");
                for (Element propertyElement : propertyElements) {
                    String name = propertyElement.attributeValue("name");
                    if("driver".equals(name)){
                        String driver = propertyElement.attributeValue("value");
                        configuration.setDriver(driver);
                    }
                    if("url".equals(name)){
                        String url = propertyElement.attributeValue("value");
                        configuration.setUrl(url);
                    }
                    if("username".equals(name)){
                        String username = propertyElement.attributeValue("value");
                        configuration.setUsername(username);
                    }
                    if("password".equals(name)){
                        String password = propertyElement.attributeValue("value");
                        configuration.setPassword(password);
                    }
                }
            }

            List<Element> propertiesSettings = rootElement.selectNodes("//settings/setting");
            for (Element propertiesSetting : propertiesSettings){
                String name = propertiesSetting.attributeValue("name");
                if("mapUnderscoreToCamelCase".equals(name)){
                    Boolean mapUnderscoreToCamelCase = Boolean.parseBoolean(propertiesSetting.attributeValue("value"));
                    configuration.setMapUnderscoreToCamelCase(mapUnderscoreToCamelCase);
                }
            }



            List<Element> mapperElements = rootElement.selectNodes("//mappers/mapper");
            for (Element mapperElement : mapperElements){
                Attribute resource = mapperElement.attribute("resource");
                if(resource != null){
                    System.out.println("使用的是XML");
                    String mapperPath = resource.getValue();
                    Map<String, SqlMapper> sqlMappers = loadMapperXMLConfiguration(mapperPath);
                    configuration.setSqlMappers(sqlMappers);
                } else {
                    System.out.println("使用的是注解");
                    String mapperClassPath = mapperElement.attributeValue("class");
                    Map<String, SqlMapper> sqlMappers = loadMapperAnnotationConfiguration(mapperClassPath);
                    configuration.setSqlMappers(sqlMappers);
                }
            }
        } catch (DocumentException | ClassNotFoundException | IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return configuration;
    }

    private static Map<String,SqlMapper> loadMapperAnnotationConfiguration(String mapperClassPath) throws ClassNotFoundException {
        Map<String, SqlMapper> sqlMappers = new HashMap<>();
        Class<?> mapperClass = Class.forName(mapperClassPath);
        Method[] methods = mapperClass.getMethods();
        for (Method  method : methods){
            boolean isAnnotation = method.isAnnotationPresent(Select.class);
            if(isAnnotation){
                SqlMapper sqlMapper = new SqlMapper();
                Select selectAnnotation = method.getAnnotation(Select.class);
                String queryString = selectAnnotation.value();
                sqlMapper.setQueryString(queryString);
                Type returnType = method.getGenericReturnType();
                if(returnType instanceof ParameterizedType){
                    ParameterizedType  pType = (ParameterizedType)returnType;
                    Type[] types = pType.getActualTypeArguments();
                    Class domanClass =(Class) types[0];
                    String resultType = domanClass.getName();
                    sqlMapper.setResultType(resultType);
                }
                String methodName = method.getName();
                String className = method.getDeclaringClass().getName();
                String key = className+ "." + methodName;
                sqlMappers.put(key,sqlMapper);
            }
        }
        return sqlMappers;
    }

    private static Map<String, SqlMapper> loadMapperXMLConfiguration(String mapperPath) {
        InputStream inputStream = null;
        try {
            Map<String, SqlMapper> sqlMappers = new HashMap<>();
            if(mapperPath.endsWith("*.xml")){
                URL url = XMLConfigBuilder.class.getClassLoader().getResource("mapper");
                assert url != null;
                File file = new File(url.getFile());
                File[] files = file.listFiles();
                for (File path : files){
                    //从本地获取文件
                    inputStream = new FileInputStream(path);
                    SAXReader reader = new SAXReader();
                    Document document = reader.read(inputStream);
                    Element rootElement = document.getRootElement();
                    String namespace = rootElement.attributeValue("namespace");
                    List<Element> selectElements  = rootElement.selectNodes("//select");
                    for (Element selectElement :selectElements ){
                        String id = selectElement.attributeValue("id");
                        String resultType = selectElement.attributeValue("resultType");
                        String querString = selectElement.getText();
                        String key = namespace + "." + id;
                        SqlMapper sqlMapper = new SqlMapper();
                        sqlMapper.setQueryString(querString);
                        sqlMapper.setResultType(resultType);
                        sqlMappers.put(key,sqlMapper);
                    }
                }
            }else{
                inputStream = Resources.getResourceAsStream(mapperPath);
                SAXReader reader = new SAXReader();
                Document document = reader.read(inputStream);
                Element rootElement = document.getRootElement();
                String namespace = rootElement.attributeValue("namespace");
                List<Element> selectElements  = rootElement.selectNodes("//select");
                for (Element selectElement :selectElements ){
                    String id = selectElement.attributeValue("id");
                    String resultType = selectElement.attributeValue("resultType");
                    String querString = selectElement.getText();
                    String key = namespace + "." + id;
                    SqlMapper sqlMapper = new SqlMapper();
                    sqlMapper.setQueryString(querString);
                    sqlMapper.setResultType(resultType);
                    sqlMappers.put(key,sqlMapper);
                }
            }
            return sqlMappers;
        }catch (Exception e){
            throw new RuntimeException(e);
        }finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
